/*+**************************************************************************/
/***                                                                      ***/
/***   This file is distributed under a BSD license.                      ***/
/***   See LICENSE.txt for details.                                       ***/
/***                                                                      ***/
/**************************************************************************+*/

material RoveShader
{
  vs asc vs_3_0
  {
    cbuffer RoveShaderVEnv : register(c0) : slot vs 0
    {
      row_major float4x4 mvp;
      row_major float4x4 mv;
      float4 EyePos;
    };

    cbuffer RoveMtrlModelPara : register(c28) : slot vs 1 // copy of Wz4MtrlModelPara
    {
      float4 m[3];
    };

    cbuffer RoveShaderVPara : register(c24) : slot vs 2
    {
      float4 texmat[4];
    };

    permute RoveShaderVPerm
    {
      Tex { TexOff,Tex1,Tex2 };
      Detail { DetailUV,DetailPos,DetailNorm,DetailRefl };
      Matrix { MatrixOff,MatrixSkin,MatrixInst };
      assert(Detail implies Tex==Tex2);
    };

    use RoveShaderVEnv;
    use RoveMtrlModelPara;
    use RoveShaderVPara;
    use RoveShaderVPerm;

    void main
    (
      in float3 in_pos : POSITION,
      in float3 in_norm : NORMAL,
      in float2 in_uv0 : TEXCOORD0 : pif(Tex),

      in int4 in_index : BLENDINDICES : pif(Matrix==MatrixSkin),
      in int4 in_weighti : BLENDWEIGHT : pif(Matrix==MatrixSkin),
      in float4 in_mat0 : TEXCOORD5 : pif(Matrix==MatrixInst),
      in float4 in_mat1 : TEXCOORD6 : pif(Matrix==MatrixInst),
      in float4 in_mat2 : TEXCOORD7 : pif(Matrix==MatrixInst),
      
      out float3 out_pos_world : TEXCOORD0,
      out float3 out_norm : TEXCOORD1,
      out float4 out_uv0 : TEXCOORD2 : pif(Tex),
      out float4 out_pos : POSITION,
      
      uniform float4 Skinning[74] : register(c32) : pif(Matrix==MatrixSkin),      
    )
    {
      pif(Matrix==MatrixSkin)
      {
        float4 sm0,sm1,sm2,n;

        float4 in_weight = float4(in_weighti-127)/127;

        sm0  = in_weight.x * Skinning[in_index.x+0];
        sm1  = in_weight.x * Skinning[in_index.x+1];
        sm2  = in_weight.x * Skinning[in_index.x+2];
        sm0 += in_weight.y * Skinning[in_index.y+0];
        sm1 += in_weight.y * Skinning[in_index.y+1];
        sm2 += in_weight.y * Skinning[in_index.y+2];
        sm0 += in_weight.z * Skinning[in_index.z+0];
        sm1 += in_weight.z * Skinning[in_index.z+1];
        sm2 += in_weight.z * Skinning[in_index.z+2];
        sm0 += in_weight.w * Skinning[in_index.w+0];
        sm1 += in_weight.w * Skinning[in_index.w+1];
        sm2 += in_weight.w * Skinning[in_index.w+2];
        
        n = float4(in_pos.xyz,1);
        in_pos.x  = dot(n,sm0);
        in_pos.y  = dot(n,sm1);
        in_pos.z  = dot(n,sm2);
        n = float4(in_norm.xyz,0);
        in_norm.x = dot(n,sm0);
        in_norm.y = dot(n,sm1);
        in_norm.z = dot(n,sm2);          
      }

      row_major float4x3 model;
      pif(Matrix==MatrixInst)
        model = transpose(float3x4(in_mat0,in_mat1,in_mat2));
      pelse
        model = transpose(float3x4(m[0],m[1],m[2]));
      float3 pos = mul(float4(in_pos.xyz,1),model);

      out_pos = mul(float4(pos,1),mvp);
      out_pos_world = mul(float4(pos,1),model);
      out_norm = mul(float4(in_norm.xyz,0),model);

      pif(Tex)
      {
        float4 uv=float4(in_uv0,0,1);
        out_uv0.x = dot(texmat[0],uv);
        out_uv0.y = dot(texmat[1],uv);
        out_uv0.zw = 0;
      }
      pif(Tex==Tex2)      // DetailUV,DetailPos,DetailNorm,DetailRefl
      {
        float4 uv;
        float3 norm = normalize(out_norm);
        pif(Detail==DetailUV)   uv=float4(in_uv0,0,1);
        pif(Detail==DetailPos)  uv=float4(in_pos,1);
        pif(Detail==DetailNorm) uv=float4(norm,1);
        pif(Detail==DetailRefl) uv=float4(reflect(normalize(pos.xyz-EyePos.xyz),norm),1);
         
        out_uv0.z = dot(texmat[2],uv);
        out_uv0.w = dot(texmat[3],uv);
      }
    }
  }

  ps asc ps_3_0
  {
    cbuffer RoveShaderPEnv : register(c0) : slot ps 0
    {
      float4 FogPara;
      float4 FogColor;
      float4 ClipPlane[4];
      
      float4 Ambient;
      float4 LightPos[3]; // xxxx,yyyy,zzzz
      float4 LightCol[3]; // rrrr,gggg,bbbb
      float4 LightInvSqRange;
    };

    permute RoveShaderPPerm
    {
      Tex1;
      Tex2 { Tex2Off,Tex2Add,Tex2Mul };
      Fog { FogOff,FogOn,FogBlack,FogClip3 };
      Clip;
      assert(Tex2 implies Tex1);
    };

    use RoveShaderPPerm;
    use RoveShaderPEnv;

    samplerCUBE sCube : register(s0);
    sampler2D s1 : register(s1) : pif(Tex1);
    sampler2D s2 : register(s2) : pif(Tex2);

    void main
    (
      in float3 in_pos : TEXCOORD0,
      in float3 in_norm  : TEXCOORD1,
      in float4 in_uv0 : TEXCOORD2 : pif(Tex1),
      out float4 out_col : COLOR0,
    )
    {
      float4 pos1 = float4(in_pos,1);

      pif(Clip)
      {
        float4 clipdot;
        clipdot.x = dot(pos1,ClipPlane[0]);
        clipdot.y = dot(pos1,ClipPlane[1]);
        clipdot.z = dot(pos1,ClipPlane[2]);
        clipdot.w = dot(pos1,ClipPlane[3]);
        clip(clipdot);
      }

      float4 light = Ambient + texCUBE(sCube,in_norm);

      // ---- lighting (4 point lights)
      // vector to light
      float4 dx = LightPos[0] - in_pos.x;
      float4 dy = LightPos[1] - in_pos.y;
      float4 dz = LightPos[2] - in_pos.z;

      // squared length, inv. squared length, attenuation
      float4 lenSq = dx*dx + dy*dy + dz*dz;
      float4 lenRSq = float4(rsqrt(lenSq.x),rsqrt(lenSq.y),rsqrt(lenSq.z),rsqrt(lenSq.w));      
      float4 attenuate = saturate(float4(1,1,1,1) - lenSq * LightInvSqRange);

      // light vector, dot products
      float4 Lx = dx * lenRSq;
      float4 Ly = dy * lenRSq;
      float4 Lz = dz * lenRSq;
      float4 NdotL = saturate(in_norm.x * Lx + in_norm.y * Ly + in_norm.z * Lz) * attenuate;

      // color
      light.r += dot(NdotL,LightCol[0]);
      light.g += dot(NdotL,LightCol[1]);
      light.b += dot(NdotL,LightCol[2]);

      // ---- textures
      out_col = light;
      pif(Tex1)             out_col *= tex2D(s1,in_uv0.xy);
      pif(Tex2 == Tex2Add)  out_col += tex2D(s2,in_uv0.zw);
      pif(Tex2 == Tex2Mul)  out_col *= tex2D(s2,in_uv0.zw);

      // ---- fogging
      pif(Fog != FogOff)
      {
        float fog = length(in_pos);
        if(Fog==FogClip3)
        {
          float d = dot(normalize(in_pos),ClipPlane[3].xyz);
          if(d < 0)
            fog = min(fog,-dot(pos1,ClipPlane[3])/-d);
          else 
            fog = min(fog,ClipPlane[3].w/-d);
        }
        fog = saturate((fog+FogPara.x)*FogPara.y);
        pif(Fog==FogClip3)
          fog = fog*fog*(3-2*fog);
        pelse
          fog = 2*fog-fog*fog;
        fog = fog*FogColor.w;
        if(Fog==FogBlack)
          out_col.xyz *= 1-fog;
        else
          out_col.xyz = lerp(out_col.xyz,FogColor,fog);
      }
    }
  }

  header
  {
    enum ExtraBits
    {
      Extra_Fog           = 0x0001,
      Extra_FogBlack      = 0x0002,
      Extra_TexMul        = 0x0004,
      Extra_Clip          = 0x0008,

      ExtraDetailMask     = 0x00f0,
      ExtraDetailUV       = 0x0000,
      ExtraDetailPos      = 0x0010,
      ExtraDetailNorm     = 0x0020,
      ExtraDetailRefl     = 0x0030,
    };
    sInt Extra;
  }
  new
  {
    Extra = 0;
  }

  prepare
  {
    sInt ps=0,vs=0;
    sU32 fm = format->GetAvailMask();
    
    if(Texture[1])
    {
      ps |= RoveShaderPPermMask_Tex1;
      if(!Texture[2])
        vs |= RoveShaderVPerm_Tex1;
    }
    if(Texture[2])
    {
      if(Extra & Extra_TexMul)
        ps |= RoveShaderPPerm_Tex2Mul;
      else
        ps |= RoveShaderPPerm_Tex2Add;
      vs |= RoveShaderVPerm_Tex2;
      switch(Extra & ExtraDetailMask)
      {
        default:
        case ExtraDetailUV:   vs |= RoveShaderVPerm_DetailUV;   break;
        case ExtraDetailPos:  vs |= RoveShaderVPerm_DetailPos;  break;
        case ExtraDetailNorm: vs |= RoveShaderVPerm_DetailNorm; break;
        case ExtraDetailRefl: vs |= RoveShaderVPerm_DetailRefl; break;
      }
    }
    if((Extra&3)==1)
      ps |= RoveShaderPPerm_FogOn;
    else if((Extra&3)==2)
      ps |= RoveShaderPPerm_FogBlack;
    else if((Extra&3)==3)
      ps |= RoveShaderPPerm_FogClip3;
    if(Extra&8)
      ps |= RoveShaderPPermMask_Clip;

    if(fm & (1<<sVF_UV7))
      vs |= RoveShaderVPerm_MatrixInst;
    else if(fm & (1<<sVF_BONEINDEX))
      vs |= RoveShaderVPerm_MatrixSkin;

    VertexShader = VS(vs);
    PixelShader = PS(ps); 
  }
};
